package org.lightdi.container.parser;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.lightdi.container.DIContainer;
import org.lightdi.container.config.ComponentConfig;
import org.lightdi.container.config.ContainerConfig;
import org.lightdi.container.config.ComponentConfig.Arg;
import org.lightdi.container.config.ComponentConfig.ConstructorArg;
import org.lightdi.container.config.ComponentConfig.InitMethod;
import org.lightdi.container.config.ComponentConfig.SetterArg;
import org.lightdi.container.meta.MetaComponent;
import org.lightdi.container.meta.MetaComponentRef;
import org.lightdi.container.tag.ComponentTag;
import org.lightdi.container.tag.ContainerTag;
import org.lightdi.container.tag.ComponentTag.ConstructorArgTag;
import org.lightdi.container.tag.ComponentTag.InitMethodTag;
import org.lightdi.container.tag.ComponentTag.SetterArgTag;
import org.lightdi.util.ObjectUtil;
import org.lightdi.util.ResourceUtil;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

public final class ConfigLoadUtil {

	public static List<Map<String, ContainerConfig>> readConfigXML(
			String configXML, Map<String, ContainerConfig> registerdConfigs,
			Map<String, ContainerConfig> registeredConfigXMLs) {

		SAXParserFactory parserFactory = SAXParserFactory.newInstance();
		XMLReader reader = null;
		try {
			SAXParser parser = parserFactory.newSAXParser();
			reader = parser.getXMLReader();
		} catch (Exception e) {
			throw new RuntimeException(
					"LightDIContainer load error! (at parser instantiation) : "
							+ e.getMessage(), e);
		}

		ConfigLoadHandler handler = new ConfigLoadHandler();
		handler.setContainerConfigs(registerdConfigs);
		handler.setIncludedConfigXMLs(registeredConfigXMLs);
		handler.setCurrentConfigXML(configXML);

		reader.setContentHandler(handler);
		reader.setDTDHandler(new DefaultHandler());
		reader.setEntityResolver(new DefaultHandler());
		reader.setErrorHandler(new DefaultHandler());
		InputStream is = null;
		try {
			is = ResourceUtil.getResourceStream(configXML);
			reader.parse(new InputSource(is));
		} catch (Exception e) {
			throw new IllegalStateException(
					"LightDIContainer load error! (at config file read) : "
							+ configXML, e);
		} finally {
			ResourceUtil.close(is);
		}
		registerdConfigs = handler.getContainerConfigs();
		registeredConfigXMLs = handler.getIncludedConfigXMLs();
		Set<String> pathes = registeredConfigXMLs.keySet();
		for (String path : pathes) {
			ContainerConfig xmlConfig = registeredConfigXMLs.get(path);
			if (xmlConfig.isLoaded())
				continue;
			parserFactory = SAXParserFactory.newInstance();
			reader = null;
			try {
				SAXParser parser = parserFactory.newSAXParser();
				reader = parser.getXMLReader();
			} catch (Exception e) {
				throw new RuntimeException(
						"LightDIContainer load error! (at parser instantiation) : "
								+ e.getMessage(), e);
			}
			ContainerConfig currentContainer = registeredConfigXMLs.get(path);
			try {
				handler = new ConfigLoadHandler();
				handler.setCurrentContainer(currentContainer);
				handler.setContainerConfigs(registerdConfigs);
				handler.setIncludedConfigXMLs(registeredConfigXMLs);
				handler.setCurrentConfigXML(path);

				reader.setContentHandler(handler);
				reader.setDTDHandler(new DefaultHandler());
				reader.setEntityResolver(new DefaultHandler());
				reader.setErrorHandler(new DefaultHandler());
				is = ResourceUtil.getResourceStream(path);
				reader.parse(new InputSource(is));
			} catch (Exception e) {
				throw new RuntimeException(
						"LightDIContainer load error! (at config file read) : "
								+ path, e);
			} finally {
				ResourceUtil.close(is);
			}
			ContainerConfig result = handler.getCurrentContainer();
			registerdConfigs.put(result.getName(), result);
			registeredConfigXMLs.put(path, result);
		}
		List<Map<String, ContainerConfig>> ret = new ArrayList<Map<String, ContainerConfig>>();
		ret.add(registerdConfigs);
		ret.add(registeredConfigXMLs);
		return ret;
	}

	public static ContainerConfig readContainerConfig(ContainerConfig src,
			Attributes attributes) {
		ContainerConfig ret = ObjectUtil.deepCopy(src);
		String containerName = attributes
				.getValue(ContainerTag.Attributes.NAME);
		if (containerName == null)
			containerName = DIContainer.DEFAULT_CONTAINER_NAME;
		ret.setName(containerName);
		ret.setLockKey(null);
		ret.setPath(attributes.getValue(ContainerTag.Attributes.PATH));
		ret.setLoaded(true);
		return ret;
	}

	public static ComponentConfig readComponentConfig(ComponentConfig src,
			Attributes attributes) {
		ComponentConfig ret = ObjectUtil.deepCopy(src);
		ret.setName(attributes.getValue(ComponentTag.Attributes.NAME));
		ret.setValue(attributes.getValue(ComponentTag.Attributes.VALUE));
		ret.setClassName(attributes.getValue(ComponentTag.Attributes.CLASS));
		String instanceType = attributes
				.getValue(ComponentTag.Attributes.INSTANCE_TYPE);
		if (instanceType == null)
			instanceType = MetaComponent.InstanceType.SINGLETON;
		ret.setInstanceType(instanceType);
		return ret;
	}

	public static Arg readArgConfig(Arg src, Attributes attributes) {
		Arg destArg = ObjectUtil.deepCopy(src);

		// ref component
		String refName = attributes.getValue(ConstructorArgTag.Attributes.REF);
		if (refName != null)
			destArg.setRef(new MetaComponentRef(refName));

		destArg.setValue(attributes
				.getValue(ConstructorArgTag.Attributes.VALUE));
		String className = attributes
				.getValue(ConstructorArgTag.Attributes.CLASS);
		destArg.setClassName(className);
		return destArg;
	}

	public static InitMethod readInitMethodConfig(InitMethod src,
			Attributes attributes) {
		InitMethod destInitMethod = ObjectUtil.deepCopy(src);

		String methodName = attributes
				.getValue(InitMethodTag.Attributes.METHOD_NAME);
		destInitMethod.setMethodName(methodName);
		return destInitMethod;
	}

	public static ConstructorArg readConstructorArgConfig(ConstructorArg src,
			Attributes attributes) {
		ConstructorArg destConstArg = ObjectUtil.deepCopy(src);

		// ref component
		String refName = attributes.getValue(ConstructorArgTag.Attributes.REF);
		if (refName != null)
			destConstArg.setRef(new MetaComponentRef(refName));

		destConstArg.setValue(attributes
				.getValue(ConstructorArgTag.Attributes.VALUE));
		String className = attributes
				.getValue(ConstructorArgTag.Attributes.CLASS);
		destConstArg.setClassName(className);
		return destConstArg;
	}

	public static SetterArg readSetterArgConfig(SetterArg src,
			Attributes attributes) {
		SetterArg destSetArg = ObjectUtil.deepCopy(src);

		// ref component
		String refName = attributes.getValue(SetterArgTag.Attributes.REF);
		if (refName != null)
			destSetArg.setRef(new MetaComponentRef(refName));

		destSetArg.setName(attributes.getValue(SetterArgTag.Attributes.NAME));
		destSetArg.setClassName(attributes
				.getValue(SetterArgTag.Attributes.CLASS));
		destSetArg.setValue(attributes.getValue(SetterArgTag.Attributes.VALUE));
		return destSetArg;
	}

	public static ContainerConfig getRegisteredOrNewContainerConfig(
			Map<String, ContainerConfig> containers, String name) {
		ContainerConfig registerd = containers.get(name);
		return registerd != null ? registerd : new ContainerConfig();
	}

	public static ComponentConfig getRegisteredOrNewComponentConfig(
			Map<String, ComponentConfig> components, String name) {
		ComponentConfig registerd = components.get(name);
		return registerd != null ? registerd : new ComponentConfig();
	}

}
